package com.xyy.saas.payment.callback.core.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.internal.util.StringUtils;
import com.unionpay.acp.sdk.SDKUtil;
import com.unionpay.acp.sdksample.multicert.DemoBase;
import com.xyy.saas.payment.adpater.channel.setting.ChannelSettingManager;
import com.xyy.saas.payment.adpater.config.BankConfigManager;
import com.xyy.saas.payment.adpater.impl.WxCommonAdpter;
import com.xyy.saas.payment.adpater.jd.common.JDConstants;
import com.xyy.saas.payment.adpater.jd.utils.JDRequestUtils;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.config.MiniCashierPayConfig;
import com.xyy.saas.payment.config.PingxxConfig;
import com.xyy.saas.payment.config.YkqAliConfig;
import com.xyy.saas.payment.cores.enums.BusinessOrderTypeEnum;
import com.xyy.saas.payment.dao.model.AlipaySettings;
import com.xyy.saas.payment.dao.model.EntrustPayInfo;
import com.xyy.saas.payment.dao.model.PayAccount;
import com.xyy.saas.payment.dao.model.WxSettings;
import com.xyy.saas.payment.service.EntrustPayInfoService;
import com.xyy.saas.payment.service.PayAccountService;
import com.xyy.saas.payment.util.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;


/**
 * @author wangtianqi
 * @date 2019/11/26 15:24
 */
@Component
@DependsOn({"pingxxConfig", "configMap","ykqWxConfig","ykqAliConfig"})
public class VerifySign {

    private static Logger LOGGER = LoggerFactory.getLogger(VerifySign.class);
    @Autowired
    private PingxxConfig pingxxConfig;
    @Autowired
    private YkqAliConfig ykqAliConfig;
    @Autowired
    private BankConfigManager bankConfigManager;
    @Autowired
    private ChannelSettingManager channelSettingManager;
    @Autowired
    private WxCommonAdpter wxCommonAdpter;
    @Autowired
    private MiniCashierPayConfig miniCashierPayConfig;
    @Autowired
    private EntrustPayInfoService entrustPayInfoService;
    @Autowired
    private PayAccountService payAccountService;

    public boolean verifyData(String channel, String method, HttpServletRequest requestMsg, String body) throws AlipayApiException {
        boolean result = false;
        switch (channel) {
            case Constants.CHANNEL_PINGXX_NAME:
                String sign = JSON.toJSONString(requestMsg.getHeader("x-pingplusplus-signature"));
                result = verifyPingxxData(sign, body);
                LOGGER.info("PlatformController#chargeCallBack sign:{}, body:{}, result:{}", sign, body, result);
                return result;
            case Constants.CHANNEL_WX_NAME:
                if("refund".equals(method)) {
                    //微信退款不走这个签名校验
                    return true;
                }
                result = isWechatSign(body);
                LOGGER.info("VerifySign verifyWx result:{}", result);
                return result;
            case Constants.CHANNEL_ALI_NAME:
                 result = isAlipaySign(requestMsg);
                 return result;
            case Constants.CHANNEL_CHINA_BANK_NAME:
                result = isChinaBankSign(requestMsg);
                return result;
            case Constants.CHANNEL_FUMIN_BANK_NAME:
            case Constants.CHANNEL_YBM_FUMIN_BANK_NAME:
            case Constants.CHANNEL_YOP_NAME:
            case Constants.CHANNEL_PINGAN_NAME:
                //富民没有验签
                return true;
            case Constants.CHANNEL_DIRECT_NAME_WX:
                // 直连微信
                if("refund".equals(method)) {
                    //微信退款不走这个签名校验
                    return true;
                }
                if("wxEntrustSuccess".equals(method)) {
                    //微信签约走以下验签方式
                    result = isDirectWechatEntrustSign(body);
                    LOGGER.info("VerifySign verifyWx result:{}", result);
                    return result;
                }
                if("wxPapPaySuccess".equals(method)) {
                    //微信代扣走以下验签方式
                    result = isDirectWechatPapPaySign(body);
                    LOGGER.info("VerifySign verifyWx result:{}", result);
                    return result;
                }
                result = isDirectWechatSign(body);
                LOGGER.info("VerifySign verifyWx result:{}", result);
                return result;
            case Constants.CHANNEL_DIRECT_NAME_ALIPAY:
                result = isDirectAlipaySign(requestMsg);
                return result;
            case Constants.CHANNEL_UNIONPAY_NAME:
                result = isDirectUnionPaySign(requestMsg);
                return result;
            case Constants.CHANNEL_JD_CARD_NAME:
                try {
                    JSONObject jsonObject = JSONObject.parseObject(body);
                    // 协议通知使用verifySignAndDecryptReturn解密
                    if (method.equals(JDConstants.CALLBACK_CONTRACT_METHOD_PREFIX)) {
                        JDRequestUtils.verifySignAndDecryptReturn("jdCardPayNotifyVerifySignContract ", jsonObject.getString("merchantNo"), body);
                    } else {
                        // 其他通知使用verifySignAndReturn解密
                        JDRequestUtils.verifySignAndReturn("jdCardPayNotifyVerifySignPayRefund ", jsonObject.getString("merchantNo"), body);
                    }
                    return true;
                } catch (Exception e) {
                    return false;
                }
            case Constants.CHANNEL_JDAGGREGATE_NAME:
                try {
                    JSONObject jsonObject = JSONObject.parseObject(body);
                    JDRequestUtils.verifySignAndReturn("jdAggratePayNotifyVerifySignPayRefund ", jsonObject.getString("merchantNo"), body);
                    return true;
                } catch (Exception e) {
                    return false;
                }
            case "jdcdt":
                try {
//                    JSONObject jsonObject = JSONObject.parseObject(body);
//                    JDRequestUtils.verifySignAndReturn("jdAggratePayNotifyVerifySignPayRefund ", jsonObject.getString("merchantNo"), body);
                    return true;
                } catch (Exception e) {
                    return false;
                }
            case Constants.CHANNEL_JD_CREDIT:
                try {
//                    JSONObject jsonObject = JSONObject.parseObject(body);
//                    JDRequestUtils.verifySignAndReturn("jdAggratePayNotifyVerifySignPayRefund ", jsonObject.getString("merchantNo"), body);
                    return true;
                } catch (Exception e) {
                    return false;
                }
            default:
                return false;
        }
    }

    private boolean isDirectWechatPapPaySign(String body) {
        SortedMap<String, String> smap;
        try {
            smap = WxUtil.doXMLParse(body);
        } catch (Exception e) {
            return false;
        }
        // 支付失败 无需验签
        if(!"SUCCESS".equals(smap.get("return_code")) || !"SUCCESS".equals(smap.get("result_code")) || !"SUCCESS".equals(smap.get("trade_state"))) {
            LOGGER.error("VerifySign#isDirectWechatPapPaySign pay fail. return_msg:{}, err_code:{}, err_code_des:{}",
                    smap.get("return_msg"), smap.get("err_code"), smap.get("err_code_des"));
            return true;
        }
        StringBuffer sb = new StringBuffer();
        Set es = smap.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (!"sign".equals(k) && null != v && !"".equals(v) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        Map<String, String> attach = JSONUtils.parse2Map(smap.get("attach"), String.class, String.class);
        String payMode = attach.get("pay_mode");
        String userId = attach.get("receiver_id");
        String businessOrderType = null;
        PayAccount payAccount = payAccountService.getPayAccountByAccountId(userId);
        if(null != payAccount) {
            businessOrderType = payAccount.getBusinessIdType();
        }

        WxSettings wxSettings =  channelSettingManager.getWXSettings(userId, businessOrderType, payMode);
        if(null == wxSettings) {
            LOGGER.error("VerifySign wxSettings is not exist. businessIdType:{}, payMode:{}", businessOrderType, payMode);
            return false;
        }

        sb.append("key=" + wxSettings.getApiSecret());
        /** 验证的签名 */
        String sign = DigestUtils.md5Hex(WxUtil.getContentBytes(sb.toString(), "utf-8")).toUpperCase();
        /** 微信端返回的合法签名 */
        String validSign = ((String) smap.get("sign")).toUpperCase();
        return validSign.equals(sign);
    }

    private boolean isDirectWechatEntrustSign(String body) {
        SortedMap<String, String> smap;
        try {
            smap = WxUtil.doXMLParse(body);
        } catch (Exception e) {
            return false;
        }
        StringBuffer sb = new StringBuffer();
        Set es = smap.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (!"sign".equals(k) && null != v && !"".equals(v) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        String contractCode = smap.get("contract_code");
        EntrustPayInfo entrustPayInfo = entrustPayInfoService.selectByContractCode(contractCode);
        String businessIdType = null;
        PayAccount payAccount = payAccountService.getPayAccountByAccountId(entrustPayInfo.getAccountId());
        if(null != payAccount) {
            businessIdType = payAccount.getBusinessIdType();
        }
        if(StringUtils.isEmpty(businessIdType)){
            return false;
        }

        WxSettings wxSettings =  channelSettingManager.getWXSettings(entrustPayInfo.getAccountId(), businessIdType, entrustPayInfo.getSignMode());
        if(null == wxSettings) {
            LOGGER.error("VerifySign wxSettings is not exist. businessIdType:{}, entrustPayInfo:{}", businessIdType, JSONUtils.toJSON(entrustPayInfo));
            return false;
        }

        sb.append("key=" + wxSettings.getApiSecret());
        /** 验证的签名 */
        String sign = DigestUtils.md5Hex(WxUtil.getContentBytes(sb.toString(), "utf-8")).toUpperCase();
        /** 微信端返回的合法签名 */
        String validSign = ((String) smap.get("sign")).toUpperCase();
        return validSign.equals(sign);
    }

    private boolean verifyPingxxData(String sign, String body) {
        if (sign == null) {
            return false;
        }
        byte[] signatureBytes = Base64.decodeBase64(sign);
        try {
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initVerify(getPubKey());
            signature.update(body.getBytes("UTF-8"));
            return signature.verify(signatureBytes);
        } catch (Exception e) {
            LOGGER.warn("VerifySign#verifyPingxxData failed sign:{} body:{}", sign, body);
            return false;
        }
    }

    public PublicKey getPubKey() throws Exception {
        String pubKeyString = pingxxConfig.getPingxxPublicKey();
        pubKeyString = pubKeyString.replaceAll("(-+BEGIN PUBLIC KEY-+\\r?\\n|-+END PUBLIC KEY-+\\r?\\n?)", "");
        byte[] keyBytes = Base64.decodeBase64(pubKeyString.getBytes("UTF-8"));
        // generate public key
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");

        return keyFactory.generatePublic(spec);
    }

    public boolean isWechatSign(String body) {
        SortedMap<String, String> smap;
        try {
            smap = WxUtil.doXMLParse(body);
        } catch (Exception e) {
            return false;
        }
        StringBuffer sb = new StringBuffer();
        Set es = smap.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (!"sign".equals(k) && null != v && !"".equals(v) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        String passbackParams = smap.get("attach");
        Map<String, String> attach = JSONUtils.parse2Map(passbackParams, String.class, String.class);
        String businessOrderType = attach.get("business_order_type");
        if (businessOrderType.equals(BusinessOrderTypeEnum.YIKUAIQIAN_DOC.getType())){
            if (ApolloUtil.getProperty(businessOrderType+".wx.newMchid").equals(smap.get("mch_id"))){
                sb.append("key=" + ApolloUtil.getProperty(businessOrderType+".wx.newkey"));
            }else {
                sb.append("key=" + ApolloUtil.getProperty(businessOrderType+".wx.key"));
            }
        }else {
            sb.append("key=" + ApolloUtil.getProperty(businessOrderType+".wx.key"));
        }
        /** 验证的签名 */
        String sign = DigestUtils.md5Hex(WxUtil.getContentBytes(sb.toString(), "utf-8")).toUpperCase();
        /** 微信端返回的合法签名 */
        String validSign = ((String) smap.get("sign")).toUpperCase();
        return validSign.equals(sign);
    }

    public boolean isDirectWechatSign(String body) {
        SortedMap<String, String> smap;
        try {
            smap = WxUtil.doXMLParse(body);
        } catch (Exception e) {
            return false;
        }
        StringBuffer sb = new StringBuffer();
        Set es = smap.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (!"sign".equals(k) && null != v && !"".equals(v) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }

//        String tradeType = smap.get("trade_type");
//        String passbackParams = smap.get("attach");
//        Map<String, String> attach = JSONUtils.parse2Map(passbackParams, String.class, String.class);
//        String businessOrderType = attach.get("business_order_type");
//        List<String> payModes;
//        payModes = wxCommonAdpter.getPayModesByTradeType(tradeType);

        WxSettings wxSettings = channelSettingManager.getWXSettingByMchId(smap.get("mch_id"));

//        for (String payMode : payModes) {
//            wxSettings  = channelSettingManager.getWXSettings(businessOrderType, payMode);
//
//            if(Boolean.parseBoolean(ApolloUtil.getProperty("ec_pop_switch_ybmmch")) && "ec_pop".equals(businessOrderType) && "1380365202".equals(smap.get("mch_id"))) {
//                wxSettings = channelSettingManager.getWXSettings("ec_self_support_mini_XS140001", "pub");
//            }
//
//            if(wxSettings != null) {
//                break;
//            }
//        }

        sb.append("key=" + wxSettings.getApiSecret());
        /** 验证的签名 */
        String sign = DigestUtils.md5Hex(WxUtil.getContentBytes(sb.toString(), "utf-8")).toUpperCase();
        /** 微信端返回的合法签名 */
        String validSign = ((String) smap.get("sign")).toUpperCase();
        return validSign.equals(sign);
    }

    public boolean isDirectAlipaySign(HttpServletRequest requestMsg) throws AlipayApiException {
        Map<String, String> params = new HashMap<>();
        Map<String, String[]> requestParams = requestMsg.getParameterMap();
        requestParams.forEach((key, values) -> {
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            params.put(key, valueStr);
        });
        String appId = params.get("app_id");
        AlipaySettings alipaySettings = channelSettingManager.getAlipaySettingsByAliAppId(appId);
        return AlipaySignature.rsaCheckV1(params, alipaySettings.getAlipayPublicKey(), "UTF-8", "RSA2");
    }

    public boolean isDirectUnionPaySign(HttpServletRequest requestMsg) throws AlipayApiException {
        Map<String, String> params = new HashMap<>();
        Map<String, String[]> requestParams = requestMsg.getParameterMap();
        requestParams.forEach((key, values) -> {
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            params.put(key, valueStr);
        });
        // 验签
        return SDKUtil.validate(params, DemoBase.encoding);

    }

    public boolean isAlipaySign(HttpServletRequest requestMsg) throws AlipayApiException {
        Map<String, String> params = new HashMap<>();
        Map<String, String[]> requestParams = requestMsg.getParameterMap();
        requestParams.forEach((key, values) -> {
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            params.put(key, valueStr);
        });
        return AlipaySignature.rsaCheckV1(params, ykqAliConfig.getPublickKey(), "UTF-8", "RSA2");
    }

    public boolean isChinaBankSign(HttpServletRequest requestMsg) {
        Map<String, Object> treeMap = new TreeMap<>();
        Map<String, String[]> requestParams = requestMsg.getParameterMap();
        requestParams.forEach((key, values) -> {
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            treeMap.put(key, valueStr);
        });

        try {
            boolean verferSignData = RSAUtil
                    .verifySHA1(SignatureUtil.getSignatureStr(treeMap), String.valueOf(treeMap.get("signature")),
                            RSAUtil.getPublicKey(bankConfigManager.getPublicKeyByMch((String)treeMap.get("mchNo"))),"UTF-8");
            if(verferSignData) {
                return true;
            }
            return false;
        } catch (Exception e) {
            LOGGER.warn("VerifySign#isChinaBankSign error treeMap:{}", JSON.toJSONString(treeMap), e);
            return false;
        }

    }


}
